#include "libXbee.h"


/* Initializing variables of lib */
char *xbee_dev = NULL;
extern FILE *f;

struct termios xbee_newconf;
struct termios xbee_oldconf;
int xbee_fd = -1;
fd_set xbee_fds;
enum xbee_error_t xbee_error = XBEE_ERROR_NULL;
struct timeval xbee_tv;

/***		FUNCTIONS 			***/

unsigned short crc_update(unsigned short crc,unsigned char data)
{
	data ^= (crc & 0xff);
	data ^= data << 4;
	return ((((unsigned short )data << 8) | ((crc>>8)&0xff))^(unsigned char)(data >> 4)^((unsigned short )data << 3));
}
unsigned short crc16(void *data, unsigned short cnt)
{
	unsigned short crc=0xff;
	unsigned char * ptr=(unsigned char *) data;
	int i;
	for (i=0;i<cnt;i++)
	{
		crc=crc_update(crc,*ptr);
		ptr++;
	}
	return crc;
}

/** 			openXBEEDev				**/
enum xbee_error_t openXBEEDev(const char *path)
{
	if(xbee_fd != -1) return XBEE_DEV_ALREADY_OPENED;
	if(!path) return XBEE_DEV_PATH_INVALID;
	struct stat st;

	/* verify if path is valid */
	if (-1 == stat (path, &st)) {
		perror("stat");
		return XBEE_DEV_PATH_INVALID;
	}

	/* verify if the device is character's device */
	if (!S_ISCHR (st.st_mode)) {
		perror("S_ISCHR");
		return XBEE_DEV_INVALID;
	}
	printf("%s : dev = %s \n",__FUNCTION__, path);

	/* copy path name into xbee_dev */
	if(xbee_dev == NULL)
		xbee_dev = (char *) malloc(strlen(path) + 1); /* bug : 1 octet less than needed for \0 ! */
	if(xbee_dev == NULL) printf("xbee_dev NULL\n");
		sprintf(xbee_dev,"%s",path);

	printf("xbee_dev = %s\n",xbee_dev);

	/* opening device on read&write & blocking mode */
	if((xbee_fd = open(xbee_dev,O_RDWR | O_NONBLOCK)) == -1) {
		perror("open");
		return XBEE_DEV_CANOT_OPEN;
	}

	//saving old config
	tcgetattr(xbee_fd,&xbee_oldconf);


	tcflush(xbee_fd,TCIFLUSH);

	//setting up new config
	xbee_newconf.c_cflag = B57600 | CRTSCTS | PARODD | CS8 | CLOCAL | CREAD ;
	xbee_newconf.c_iflag = IGNPAR;

	tcsetattr(xbee_fd,TCSANOW,&xbee_newconf);
	fcntl(xbee_fd,F_SETFL,10);

    	FD_ZERO (&xbee_fds);
	FD_SET (xbee_fd,&xbee_fds);
	// init timeval
	xbee_tv.tv_sec = 3; 
	xbee_tv.tv_usec = 0;
	return XBEE_ERROR_NULL;
}

enum xbee_error_t closeXBEEDev()
{
	if(xbee_fd == -1) return XBEE_DEV_NOT_INIT;

	tcflush(xbee_fd, TCIFLUSH);
	tcsetattr(xbee_fd,TCSANOW,&xbee_oldconf);
	close (xbee_fd);
	//free the fds
	FD_ZERO(&xbee_fds);
	bzero(&xbee_dev, sizeof (xbee_dev));
	xbee_fd = -1;
	return XBEE_ERROR_NULL;
}

int readXBEEDev(void *buffer, struct timeval *tv)
{
	char  query = ' ';
	short temp = 0,
	      query2 = 0;
	int   preamb_ok = 1;

	if(!buffer){
		xbee_error = XBEE_BUF_MUST_INIT;
		return -1;
	}
	int rflag = -1;
	int nread = -1;
	if(!tv) tv = &xbee_tv;
	//reseting buffer data

	rflag = select(xbee_fd+1,&xbee_fds,NULL,NULL,tv);

	switch(rflag){
		case -1:
			xbee_error = XBEE_READ_POLL_ERR;
			nread = -1;
			break;
		case 0:
			xbee_error = XBEE_READ_TIMEOUT_FIRED;
			nread = -1;
			fprintf(f,"%s : read timeout !\n",__FUNCTION__);
			break;
		default:
			do{
				nread = read(xbee_fd, &query, 1);
				if (query =='>'){
					nread = read(xbee_fd, &query, 1);
					if (query =='*'){
						nread = read(xbee_fd, &query, 1);
						if(query == '>') preamb_ok = 0;
					}
				}
			}while(preamb_ok);

			preamb_ok = 1;
			query2 = 0;
			/* lecture longueur de la trame */
			nread = read(xbee_fd, &query2, 2);
			temp = query2;
			nread = 0;

			do{
				nread = read(xbee_fd,buffer+nread,135 /*sizeof(buffer)*/);
			}while(query2!=query2-nread);

			//flushing out when all data read
			tcflush(xbee_fd,TCIFLUSH);

			(nread  != -1 ) ? (xbee_error = XBEE_ERROR_NULL) : (xbee_error = XBEE_READ_ERR);

#ifdef DEBUG
			int i;
			fprintf(f,"%s","\n TRAME RECUE \n");
			fprintf(f,"%s"," valeur lue \n");
			for(i=0;i<temp/*nread*/;i++){
				fprintf(f,"  %x ",*(unsigned char*)(buffer+i));
				if (((i+1)%10)==0) fprintf(f,"%s","\n");
			}
#endif
			break;
	}
	return (temp + 3);
}

int writeXBEEDev(void *buffer,enum xbee_packet_descriptor_t packet_descriptor, unsigned short length, struct timeval *tv)
{
	if(!buffer){
		xbee_error = XBEE_BUF_MUST_INIT;
		return -1;
	}
	int wflag = -1;
	int nwritten = -1;
	int n = 0;
	unsigned short crc;
	unsigned char *ptr=NULL;
	unsigned char lhight = length>>8;
	unsigned char llow   = length&0xff;
	unsigned char crcLow;
	unsigned char crcHight;
	union scientific_packet_t packet;

	switch(packet_descriptor){
		case PD_SCIENTIFICCOMMAND:
			packet.cmdData = *((struct SCIENTIFIC_COMMANDDATA*) buffer);
			ptr = &packet.cmdData.packetdescriptor;
			break;
		case PD_SCIENTIFICSTATUS:
			packet.statusData = *((struct SCIENTIFIC_STATUSDATA*) buffer);
			ptr = &packet.statusData.packetdescriptor;
			break;
		case PD_SCIENTIFICDATA:
			packet.data = *((struct SCIENTIFIC_DATA*) buffer);
			ptr = &packet.data.packetdescriptor;
			break;
		case PD_SCIENTIFICCONFIG:
			packet.config = *((struct SCIENTIFIC_CONFIG*) buffer);
			ptr = &packet.config.packetdescriptor;
			break;
	}

	crc = crc16(ptr,length);
	crcHight = crc>>8;
	crcLow = crc&0xff;

	if(!tv) tv = &xbee_tv;
	wflag = select(xbee_fd+1,NULL,&xbee_fds,NULL,tv);

	switch(wflag){
		case -1:
			xbee_error = XBEE_WRITE_POLL_ERR;
			nwritten = -1;
			break;
		case 0:
			xbee_error = XBEE_WRITE_TIMEOUT_FIRED;
#ifdef DEBUG
			fprintf(f,"%s : write timeout !\n",__FUNCTION__);
#endif
			nwritten = -1;
			break;
		default:
			if((nwritten = write(xbee_fd,">*>",3)) == -1){
				perror("write header");
				return -1;
			}

			if(tcdrain(xbee_fd)) perror("tcdrain1");
			n += nwritten;

			if((nwritten = write(xbee_fd,(void *)&lhight,sizeof(lhight))) == -1){
				perror("write hight byte");
				return -1;
			}

			if(tcdrain(xbee_fd)) perror("tcdrain2");
			n += nwritten;
			if((nwritten = write(xbee_fd,(void *)&llow,sizeof(llow))) == -1){
				perror("write low byte");
				return -1;
			}

			if(tcdrain(xbee_fd)) perror("tcdrain3");
			n += nwritten;
			if((nwritten = write(xbee_fd,buffer,length)) == -1){
				perror("write");
				return -1;
			}

			if(tcdrain(xbee_fd)) perror("tcdrain4");
			n += nwritten;
			if((nwritten = write(xbee_fd,(void *)&crcHight,sizeof(crcHight))) == -1){
				perror("write");
				return -1;
			}

			if(tcdrain(xbee_fd)) perror("tcdrain5");
			n += nwritten;
			if((nwritten = write(xbee_fd,(void *)&crcLow,sizeof(crcLow))) == -1){
				perror("write");
				return -1;
			}
			//wait for all data to be written

			if(tcdrain(xbee_fd)) perror("tcdrain6");
			n += nwritten;

			(n  > 0 ) ? (xbee_error = XBEE_ERROR_NULL): (xbee_error = XBEE_WRITE_ERR);
#ifdef DEBUG
			fprintf(f,"%s","\n TRAME EMISE \n");
//			fprintf(f,"length MSB  = 0x%x \n",lhight);
//			fprintf(f,"length LSB  = 0x%x \n",llow);

			int i;
			for(i=0;i<length;i++)
			{
				fprintf(f,"  %x ",(*((unsigned char *)buffer+i)));
				if (((i+1)%10)==0) fprintf(f,"%s","\n");
			}
			fprintf(f,"%s","\n");

//			fprintf(f,"crc MSB    = 0x%x \n",crcHight);
//			fprintf(f,"crc LSB    = 0x%x \n",crcLow);
			fprintf(f,"n= %d \n",n);
#endif
			break;
	}
	return n;
}
